/**
 * @author
 * Júlio Zynger
 * DRE: 110092466
 */
package model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;

import controller.Brzozowski;
import controller.simboloForaDoAlfabetoException;

public class AFD {
	static final String VAZIO = "@";
	private List<String> alfabeto;
	private LinkedHashMap<String, Estado> Q;
	private Estado inicial, parei;
	private ArrayList<String> seqConf;
	private ArrayList<Estado> seqEst;
	private LinkedHashMap<String, Estado> F;
	
	public AFD(List<String> alfabeto, LinkedHashMap<String, Estado> Q, Estado inicial, LinkedHashMap<String, Estado> F){
		this.alfabeto = alfabeto;
		this.Q = Q;
		this.inicial = inicial;
		this.F = F;
		this.seqConf = new ArrayList<String>();
		this.seqEst = new ArrayList<Estado>();
		
		for (Estado est : this.F.values()) {
			est.setFinal(true);
		}
	}
	
	public boolean computar(String palavra) throws simboloForaDoAlfabetoException{
		if(palavra.length() == 0){
			testaEstado(this.inicial);
			this.setParei(this.inicial);
			
			if (this.inicial.isFinal()) return true;
			else return false;
		}
		
		Estado prox = transiciona(inicial, palavra); 
		
		while(palavra.length() > 0){
			//System.out.println("Estou em: " + prox.toString()+ "\n\n");
			
			
			this.seqConf.add(palavra);
			palavra = palavra.substring(1);
			
			try{
				this.seqEst.add(prox);
				if(palavra.length()>0) prox = transiciona(prox, palavra);
			}catch(IndexOutOfBoundsException e){
				e.printStackTrace();
				break;
			}
			
		}
		
		consertaSequenciaEstados();
		
		this.setParei(prox);
		testaEstado(prox);
		
		if (prox.isFinal()) return true;
		
		return false;
	}

	public String brzozowski(){
		/*LinkedHashMap<Estado, String> expressoes = new LinkedHashMap<Estado, String>();
		for (Estado e : this.Q.values()) {
			expressoes.put(e, e.getLinguagem());
		}
		
		final Set<Entry<Estado, String>> mapValues = expressoes.entrySet();
	    final int maplength = mapValues.size();
	    @SuppressWarnings("unchecked")
		final Entry<Estado,String>[] entry = new Entry[maplength];
	    mapValues.toArray(entry);
		
	    imprimeExpressoes("original", expressoes);
	    
		for (int i = maplength-1; i >= 0; i--) {
			String s;
			s = entry[i].getValue();
			
			for (int j = maplength-1; j > i; j--) {
				s = s.replaceAll("L\\("+entry[j].getKey().getNome()+"\\)", "("+entry[j].getValue()+")");
			}
			
			s = arden(entry[i].getKey(), s);
			s = simplificar(s);
			entry[i].setValue(s);
		}
		imprimeExpressoes("alteracao", expressoes);
		*///TODO
		return new Brzozowski(this).brzozowski();
	}
	
	public void imprimeExpressoes(String msg, LinkedHashMap<Estado, String> expressoes){
		System.out.println("------"+msg+"--------");
		for (Iterator<String> it = expressoes.values().iterator(); it.hasNext();) {
			String z = (String) it.next();
			System.out.println(z);
		}
		//TODO deleteme!
	}
	
	public String arden(Estado sobreOQual, String exp){
		String[] pedacos = exp.split("U");
		
		int g = 0;
		for (int i = 0; i < pedacos.length; i++) {
			if(!pedacos[i].contains("L("+sobreOQual.getNome()+")")){
				g++;
			}
		}
		if(g == pedacos.length) return exp;
		
		pedacos = ordenaVetorLinguagem(sobreOQual, pedacos);
		
		String estrelado = pedacos[0].replace("L("+sobreOQual.getNome()+")", "");
		
		String outros = "";
		for (int i = 1; i < pedacos.length; i++) {
			outros+="U"+pedacos[i];
		}		
		
		outros = "("+outros.substring(1)+")";
		
		if(estrelado.length()==1) return estrelado+"*"+outros;
		return "("+estrelado+")*"+outros;
	}
	
	public String simplificar(String s){
		s = removerParentesisComUnicoSimbolo(s);
		return s;
	}
	
	public String distributiva(String s){
		
		return s;
	}
	
	public String removerParentesisComUnicoSimbolo(String s){
		for (int i = 0; i < s.length()-2; i++) {
			if(s.charAt(i) == '(' && s.charAt(i+2) == ')'){
				s = s.replace("("+s.charAt(i+1)+")", String.valueOf(s.charAt(i+1)));
				i = i-2;
			}
		}
		return s;
		
	}
	
	public String[] ordenaVetorLinguagem(Estado e, String[] vet){
		ArrayList<String> al = new ArrayList<String>();
		for (String s : vet) {
			al.add(s);
		}
		int cont = 0;
		for (String s : al) {
			if (s.contains(e.getNome())) break;
			cont++;
		}
		
		ArrayList<String> ret = new ArrayList<String>();
		ret.add(vet[cont]);
		for (String s : al) {
			if(!s.equals(vet[cont])) ret.add(s);
		}
		
		String[] retorno = new String[ret.size()];
		int i = 0;
		for (String s : ret) {
			retorno[i] = s;
			i++;
		}
		
		return retorno;
	}
	
	public String kleene(){
		String[][][] tabela = getTabelaInicialKleene();
		
		for (int k = 1; k <= tabela.length-1; k++) {
			for (int i = 0; i < tabela.length-1; i++) {
				for (int j = 0; j < tabela.length-1; j++) {
					String pt1 = tabela[i][j][k-1];
					String pt21 = tabela[i][k-1][k-1];
					String pt22 = tabela[k-1][k-1][k-1];
					String pt23 = tabela[k-1][j][k-1];
					String s="";
					
					String[] str = new String[4];
					
					if(!pt1.equals(VAZIO)) str[0] = pt1;
					else str[0] = "";
					
					if(pt21.equals(VAZIO) || pt23.equals(VAZIO)) str[1] = str[2] = str[3] = "";
					else if (!pt21.equals(VAZIO) && !pt23.equals(VAZIO)) str[1] = pt21; str[3] = pt23;
					
					if(pt22.equals(VAZIO)) str[2] = "";
					else str[2] = pt22;
					
					if(!str[0].equals("")){
						if(str[1].equals("")||str[3].equals("")){
							s = str[0];
						}
						else{
							if(str[2].equals("")){
								s = str[0]+"U"+str[1]+str[3];
							}else{
								if(str[2].length()==1) s = str[0]+"U"+str[1]+str[2]+"*"+str[3];
								else s = str[0]+"U"+str[1]+"("+str[2]+")*"+str[3];
							}
						}
					}else{
						if(str[1].equals("")||str[3].equals("")){
							s = VAZIO;
						}
						else{
							if(str[2].equals("")){
								s = str[1]+str[3];
							}else{
								if(str[2].length()==1) s = str[1]+str[2]+"*"+str[3];
								else s = str[1]+"("+str[2]+")*"+str[3];
							}
						}
					}
					tabela[i][j][k] = s;
				}
			}
		}
		
		int i = 0;
		for (Estado ini : this.getEstados()) {
			if(ini.equals(this.getInicial())){
				break;
			}
			i++;
		}
		
		int cont = 0;
		String s = "";
		System.out.println("Q:    "+this.Q.values());
		System.out.println("F:    "+this.F.values());
		for (Estado e : this.getQ().values()) {
			if(this.getF().get(e.getNome()) != null){
				s += "U"+tabela[i][cont][tabela.length-1];
			}
			cont++;
		}
		System.out.println(s);
		if(s.length()>0) s = s.substring(1);
		else s = VAZIO;
		return s;
	}
	
	public String[][][] getTabelaInicialKleene(){
		String[][][] tabela = new String[this.getEstados().size()+1][this.getEstados().size()+1][this.getEstados().size()+1];
		for (int i = 0; i < tabela.length-1; i++) { //vou setar todos os valores para ""
			for (int j = 0; j < tabela.length-1; j++) {
				for (int k = 0; k <= tabela.length-1; k++) {
					tabela[i][j][k] = "";
				}
			}
		}
		
		Object[] listaEstados = this.getEstados().toArray();
		
		for (int i = 0; i < this.getEstados().size(); i++) {
			Estado de = (Estado) listaEstados[i];
			for (int j = 0; j < this.getEstados().size(); j++) {
				Estado para = (Estado) listaEstados[j];
				Set<String> setSimbolos = de.getKeysByValue(para);
				if(setSimbolos.size()<1) tabela[i][j][0] = VAZIO;
				else{
					for (Iterator<String> iterator = setSimbolos.iterator(); iterator
							.hasNext();) {
						String string = (String) iterator.next();
						tabela[i][j][0] += "U"+string; //se houver mais de uma transicao
					}
					tabela[i][j][0] = tabela[i][j][0].substring(1, tabela[i][j][0].length()); //remover o primeiro U desnecessario
					if (i==j){
						tabela[i][j][0] += "Uε";
						if(tabela[i][j][0].length()==2) tabela[i][j][0] = tabela[i][j][0].substring(1); //TODO tá certo?
					}
				}
			}
		}
		
		/**
		 * printar a tabela completa
		 */
		/*for (int i = 0; i < listaEstados.length; i++) {
			String s = ((Estado) listaEstados[i]).getNome();
			for (int j = 0; j < listaEstados.length; j++) {
				s += "   " + tabela[i][j][0];
			}
			System.out.println(s);
		}*/
		
		return tabela;
	}
	
	public void consertaSequenciaEstados(){
		ArrayList<Estado> al = new ArrayList<Estado>();
		al.add(this.inicial);
		for (Estado estado : this.seqEst) {
			al.add(estado);
		}
		this.seqEst = al;
	}
	
	public Estado transiciona(Estado ondeEstou, String palavra) throws simboloForaDoAlfabetoException{
		char simbolo = palavra.charAt(0);
		Estado proximoEstado = ondeEstou.getTransicoes().get(String.valueOf(simbolo));
		if (proximoEstado == null) throw new simboloForaDoAlfabetoException("O símbolo '" + simbolo + "' não pertence ao alfabeto do autômato");
		return proximoEstado;
	}

	public void testaEstado(Estado testar){
		for (Estado es : this.F.values()) {
			if(es.getNome().equals(testar.getNome())) testar.setFinal(true);
		}
	}
	
	public Estado getInicial() {
		return inicial;
	}

	public Collection<Estado> getEstados(){
		return this.getQ().values();
	}
	
	public void setInicial(Estado inicial) {
		this.inicial = inicial;
	}

	public List<String> getAlfabeto() {
		return alfabeto;
	}

	public HashMap<String, Estado> getQ() {
		return Q;
	}

	public HashMap<String, Estado> getF() {
		return F;
	}

	public Estado getParei() {
		return parei;
	}

	public void setParei(Estado parei) {
		this.parei = parei;
	}

	public ArrayList<Estado> getSeqEst() {
		return seqEst;
	}

	public ArrayList<String> getSeqConf() {
		return seqConf;
	}
	
}
